﻿using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace DrawTools.Editor.CanvasFigures
{
  /// <summary>
  ///   Base class for all draw objects
  /// </summary>
  public abstract class CanvasObject
  {
    private const int DEFAULT_BORDER_WIDTH = 3;
    private const LineType DEFAULT_LINE_TYPE = LineType.Solid;
    protected Color TrackerColor = Color.DodgerBlue;

    protected CanvasObject()
    {
      BorderColor = Color.Black;
      BorderWidth = DEFAULT_BORDER_WIDTH;
      LineType = DEFAULT_LINE_TYPE;
    }

    #region Methods

    public void DrawTracker(Graphics graphics)
    {
      if (!IsSelected)
        return;

      using (var brush = new SolidBrush(TrackerColor))
      using (var pen = new Pen(Color.White, 4))
      {
        for (int i = 1; i <= HandlesCount; i++)
        {
          Rectangle rect = GetHandleRectangle(i);

          graphics.DrawEllipse(pen, rect);
          graphics.FillEllipse(brush, rect);
        }
      }
    }

    protected Rectangle GetHandleRectangle(int handleNumber)
    {
      Point point = GetHandle(handleNumber);

      return new Rectangle(point.X - 3, point.Y - 3, 7, 7);
    }

    private DashStyle GetDashStyle()
    {
      switch (LineType)
      {
        case LineType.Solid:
          return DashStyle.Solid;

        case LineType.Dash:
          return DashStyle.Dash;

        case LineType.Dot:
          return DashStyle.Dot;
      }

      return DashStyle.Solid;
    }

    #endregion

    #region Virtual Functions

    public abstract Rectangle GetBounds();

    public abstract void Draw(Graphics graphics);

    /// <summary>
    ///   Hit test.
    ///   Return value: -1 - no hit
    ///   0 - hit anywhere
    ///   > 1 - handle number
    /// </summary>
    public virtual int HitTest(Point point)
    {
      return -1;
    }

    /// <summary>
    ///   Get curesor for the handle
    /// </summary>
    /// <param name="handleNumber"></param>
    /// <returns></returns>
    public virtual Cursor GetHandleCursor(int handleNumber)
    {
      return Cursors.Default;
    }

    /// <summary>
    ///   Test whether object intersects with rectangle
    /// </summary>
    /// <param name="rectangle"></param>
    /// <returns></returns>
    public virtual bool IntersectsWith(Rectangle rectangle)
    {
      return false;
    }

    /// <summary>
    ///   Move object
    /// </summary>
    /// <param name="deltaX"></param>
    /// <param name="deltaY"></param>
    public virtual void Move(int deltaX, int deltaY) {}

    /// <summary>
    ///   Move handle to the point
    /// </summary>
    /// <param name="point"></param>
    /// <param name="handleNumber"></param>
    public virtual void MoveHandleTo(Point point, int handleNumber)
    {
      MoveHandleTo(point, handleNumber, false);
    }

    /// <summary>
    ///   Move handle to the point
    /// </summary>
    /// <param name="point"></param>
    /// <param name="handleNumber"></param>
    /// <param name="shiftPressed"></param>
    public virtual void MoveHandleTo(Point point, int handleNumber, bool shiftPressed) {}

    /// <summary>
    ///   Normalize object.
    ///   Call this function in the end of object resizing.
    /// </summary>
    public virtual void Normalize() {}

    protected virtual Pen CreateBorderPen()
    {
      var pen = new Pen(BorderColor, BorderWidth)
      {
        DashStyle = GetDashStyle()
      };

      return pen;
    }

    protected virtual Point GetHandle(int handleNumber)
    {
      return new Point(0, 0);
    }

    /// <summary>
    ///   Test whether point is inside of the object
    /// </summary>
    /// <param name="point"></param>
    /// <returns></returns>
    protected virtual bool PointInObject(Point point)
    {
      return false;
    }

    #endregion

    #region Shadow Effects

    protected static void DrawFuzzyPath(Graphics graphics, GraphicsPath gfxPath, Color outlineColor, int distance = 4, byte alpha = 200, int shadowWidth = 20,
                                        int opaqueWidth = 2)
    {
      SmoothingMode oldSoothingMode = graphics.SmoothingMode;
      graphics.SmoothingMode = SmoothingMode.AntiAlias;

      // Draw the shadow.
      graphics.TranslateTransform(distance, distance);
      DrawPathWithFuzzyLine(gfxPath, graphics, Color.Black, alpha, shadowWidth, opaqueWidth);
      graphics.ResetTransform();

      //// обводка фигуры
      //using (var thickPen = new Pen(Color.FromArgb(80, outlineColor), 3))
      //{
      //  graphics.DrawPath(thickPen, gfxPath);
      //}

      graphics.SmoothingMode = oldSoothingMode;
    }

    private static void DrawPathWithFuzzyLine(GraphicsPath path, Graphics gr, Color baseColor, int maxOpacity, int width, int opaqueWidth)
    {
      int numSteps = width - opaqueWidth + 1; // Number of pens we will use.
      float delta = (float)maxOpacity / numSteps / numSteps; // Change in alpha between pens.
      float alpha = delta; // Initial alpha.

      for (int thickness = width; thickness >= opaqueWidth; thickness--)
      {
        Color color = Color.FromArgb((int)alpha, baseColor.R, baseColor.G, baseColor.B);

        using (var pen = new Pen(color, thickness))
        {
          pen.EndCap = LineCap.Round;
          pen.StartCap = LineCap.Round;
          gr.DrawPath(pen, path);
        }
        alpha += delta;
      }
    }

    #endregion

    #region Properties

    public virtual int HandlesCount
    {
      get { return 0; }
    }

    public bool IsSelected { get; set; }

    public Color BorderColor { get; set; }

    public int BorderWidth { get; set; }

    public LineType LineType { get; set; }
    
    #endregion
  }
}